package server;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.StringTokenizer;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManagerFactory;
import javax.security.cert.X509Certificate;

import common.Buffer;
import common.PasswordStore;
import common.Receiver;
import common.Sender;

public class Server {
	private SSLServerSocket theServerSocket;
	private SSLSocket socket;
	private boolean auth;
	private String cert_user;
	
	public Server(){
		auth = false;
		
		Auditor auditor = new Auditor();
		
		Storage s = null;
		try{
			// Read from disk using FileInputStream
			FileInputStream f_in = new 
				FileInputStream("storage");
	
			// Read object using ObjectInputStream
			ObjectInputStream obj_in = 
				new ObjectInputStream (f_in);
	
			// Read an object
			s = (Storage) obj_in.readObject();
		}catch(Exception e){
			e.printStackTrace();
		}
		
		ArrayList<User> users = s.users;
		PasswordStore pws = s.pws;
		ArrayList<Journal> j = s.journals;
		ArrayList<Patient> p = s.patients;
		
		setUpSocket();
		Buffer inputBuffer = new Buffer();
		Receiver receiver = new Receiver(socket, inputBuffer);
		receiver.start();
		Sender sender = new Sender(socket);
		sender.start();
		String user = inputBuffer.poll(); //receive username
		User currentuser = null;
		for (User u : users){
			if (u.getName().equals(user)){
				currentuser = u;
				break;
			}
		}
		
		sender.send(pws.challenge()); //send challenge
		sender.send(pws.getSalt(user)); //send salt
		String response = inputBuffer.poll(); //receive response
		auth = pws.verify(user, response) && (user.equals(cert_user));
		if (!auth){
			print("User authentication incorrect! Aborting");
			auditor.logon(user, false);
			sender.send("0");
			System.exit(1);
		}
		print("User authenticated");
		auditor.logon(user, true);
		sender.send("1");
		
		ShellListener sl = new ShellListener(sender, inputBuffer, j, p, currentuser, auditor, s);
		sl.start();
	}

	private void setUpSocket(){
		SSLServerSocketFactory wtf = null;
		KeyStore ks = null;
		
		try{
			SSLContext ctx;
			KeyManagerFactory kmf;
			char[] passphrase = "password".toCharArray();
	
			ctx = SSLContext.getInstance("TLS");
			kmf = KeyManagerFactory.getInstance("SunX509");
			ks = KeyStore.getInstance("JKS");
			
			KeyStore localTrustStore = KeyStore.getInstance("JKS");
			localTrustStore.load(new FileInputStream("truststore"), "password".toCharArray());
			
			TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
			
			tmf.init(localTrustStore);
	
			ks.load(new FileInputStream("serverskeystore"), passphrase);
	
			kmf.init(ks, passphrase);
			ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
			
			wtf = ctx.getServerSocketFactory();
			theServerSocket = (SSLServerSocket) wtf.createServerSocket(2000);
			theServerSocket.setNeedClientAuth(true);
		}catch (Exception e){
			e.printStackTrace();
		}
		
		socket = null;
		print("Waiting for connection from client");
		try {
			socket = (SSLSocket)(theServerSocket.accept());
		} catch (IOException e) {
			e.printStackTrace();
			return;
		}
		socket.setNeedClientAuth(true);
		print("Connection established. Waiting for certificate");
		
		
		SSLSession session = socket.getSession();
		X509Certificate cert = null;
		try {
			cert = (X509Certificate)session.getPeerCertificateChain()[0];
		} catch (SSLPeerUnverifiedException e) {
			e.printStackTrace();
			return;
		}
		print("Certificate verified");
		
		String subject = cert.getSubjectDN().getName();
		StringTokenizer toke = new StringTokenizer(subject, "=,");
		toke.nextToken();
		cert_user = toke.nextToken(); //CN of the cert
	}

	private void print(String text) {
		System.out.println("EIT060 SERVER: " + text);
	}
}
